/* generate-octicons
 *
 * Utility script for generating a strongly typed representation of all
 * octicons distributed by the octicons NPM package. Enumerates the icons
 * and generates the TypeScript class containing just what Desktop needs.
 */

import * as fs from 'fs'
import * as Path from 'path'
import * as cp from 'child_process'
import { check } from 'reserved-words'
import toCamelCase from 'to-camel-case'

// Basic type for the @primer/octicons package.
type OcticonsLib = Record<
  string,
  {
    readonly symbol: string
    readonly heights: Record<
      string,
      {
        readonly ast: {
          readonly children: ReadonlyArray<{
            readonly attributes: {
              readonly d: string
            }
          }>
        }
        readonly options: {
          readonly height: string
          readonly width: string
        }
      }
    >
  }
>

interface IOcticonVariant {
  p: string[]
  h: number
  w: number
}

interface IOcticon {
  /** JS friendly name of the icon. */
  readonly name: string

  /** Size variants of the icon. */
  readonly variants: Record<PropertyKey, IOcticonVariant>
}

function getJsFriendlyName(name: string) {
  const sanitizedName = toCamelCase(name)
  return check(sanitizedName, 'es6', true) ? sanitizedName + '_' : sanitizedName
}

async function generateIconData(): Promise<ReadonlyArray<IOcticon>> {
  const octicons: OcticonsLib = require('@primer/octicons')

  return Object.keys(octicons)
    .sort()
    .map(name => {
      const octicon = octicons[name]

      if (Object.keys(octicon.heights).length === 0) {
        throw new Error(`Unexpected empty sizes array for ${octicon.symbol}`)
      }

      const variants: Record<PropertyKey, IOcticonVariant> = {}

      Object.entries(octicon.heights).forEach(([height, data]) => {
        variants[height] = {
          p: data.ast.children.map(c => c.attributes.d),
          h: parseInt(data.options.height, 10),
          w: parseInt(data.options.width, 10),
        }
      })

      return { name: getJsFriendlyName(name), variants }
    })
}

generateIconData().then(results => {
  console.log(`Writing ${results.length} octicons...`)

  const out = fs.createWriteStream(
    Path.resolve(__dirname, '../app/src/ui/octicons/octicons.generated.ts'),
    {
      encoding: 'utf-8',
    }
  )

  out.write(`/*
* This file is automatically generated by the generate-octicons tool.
* Manually changing this file will only lead to sadness.
*/

export type OcticonSymbolVariant = {
  /** SVG path element data */
  readonly p: string[]

  /** The width of the symbol */
  readonly w: number

  /** The height of the symbol */
  readonly h: number
}

export type OcticonSymbolVariants = Record<PropertyKey, OcticonSymbolVariant>

export type OcticonSymbol = OcticonSymbolVariant | OcticonSymbolVariants\n\n`)

  results.forEach(icon => {
    out.write(
      `export const ${icon.name}: OcticonSymbolVariants = ${JSON.stringify(
        icon.variants
      )}\n`
    )
  })

  out.end()

  console.log('Ensuring generated file is formatted correctly...')
  const root = Path.dirname(__dirname)
  const yarnExecutable = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'
  return cp.spawn(yarnExecutable, ['lint:fix'], { cwd: root, stdio: 'inherit' })
})
